/*
* Copyright (C) 2015 Mert Şimşek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package co.mobiwise.playerview;


import co.mobiwise.playerview.utils.Utils;
import com.mobiwise.playerview.ResourceTable;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.element.Element;
import ohos.agp.render.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.multimodalinput.event.TouchEvent;

import java.util.Timer;
import java.util.TimerTask;

public class MusicPlayerView extends Component implements Component.DrawTask,
        Component.EstimateSizeListener, Component.TouchEventListener {

  /**
   * Rect for get time height and width
   */
  private static Rect mRectText;

  /**
   * Paint for drawing left and passed time.
   */
  private static Paint mPaintTime;

  /**
   * RectF for draw circle progress.
   */
  private RectFloat rectF;

  /**
   * Paint for circle progress left
   */
  private static Paint mPaintProgressEmpty;

  /**
   * Paint for circle progress loaded
   */
  private static Paint mPaintProgressLoaded;

  /**
   * Modified OnClickListener. We do not want all view click.
   * notify onClick() only button area touched.
   */
  private ClickedListener onClickListener;

  /**
   * Button paint for play/pause control button
   */
  private static Paint mPaintButton;



  /**
   * Play/Pause button region for handle onTouch
   */
  private static Region mButtonRegion;

  /**
   * Paint to draw cover photo to canvas
   */
  private static Paint mPaintCover;

  /**
   * Bitmap for shader.
   */
  private PixelMap mBitmapCover;

  /**
   * Shader for make drawable circle
   */
  private PixelMapShader mShader;

  /**
   * Scale image to view width/height
   */
  private float mCoverScale;

  /**
   * Image Height and Width values.
   */
  private int mHeight;
  private int mWidth;

  /**
   * Center values for cover image.
   */
  private float mCenterX;
  private float mCenterY;

  /**
   * Cover image is rotating. That is why we hold that value.
   */
  private int mRotateDegrees;



  /**
   * isRotating
   */
  private boolean isRotating;

  /**
   * Handler will post runnable object every @ROTATE_DELAY seconds.
   */
  private static int ROTATE_DELAY = 10;

  /**
   * 1 sn = 1000 ms
   */
  private static int PROGRESS_SECOND_MS = 1000;

  /**
   * mRotateDegrees count increase 1 by 1 default.
   * I used that parameter as velocity.
   */
  private static int VELOCITY = 1;

  /**
   * Default color code for cover
   */
  private Color mCoverColor = Color.GRAY;

  /**
   * Play/Pause button radius.(default = 120)
   */
  private float mButtonRadius = 120f;

  /**
   * Play/Pause button color(Default = dark gray)
   */
  private Color mButtonColor = Color.RED;

  /**
   * Color code for progress left.
   */
  private Color mProgressEmptyColor = Color.WHITE;

  /**
   * Color code for progress loaded.
   */
  private Color mProgressLoadedColor = Color.GREEN;

  /**
   * Time text size
   */
  private int mTextSize = 40;

  /**
   * Default text color
   */
  private Color mTextColor = Color.WHITE;

  /**
   * Current progress value
   */
  private int currentProgress = 0;

  /**
   * Max progress value
   */
  private int maxProgress = 100;

  /**
   * Auto progress value start progressing when
   * cover image start rotating.
   */
  private boolean isAutoProgress = true;

  /**
   * Progressview and time will be visible/invisible depends on this
   */
  private boolean mProgressVisibility = true;

  /**
   * play pause animation duration
   */
  private static final long PLAY_PAUSE_ANIMATION_DURATION = 200;


  /**
   * Animator set for play pause toggle
   */
  //private AnimatorSet mAnimatorSet;

  private boolean mFirstDraw = true;

  /**
   * Constructor
   */

  public MusicPlayerView(Context context) {
    super(context);
    init(context);
    // 获取屏幕的宽高度、中心点坐标，调用onEstimateSize方法
    setEstimateSizeListener(this);
    // 添加绘制任务，调用onDraw方法
    addDrawTask(this);
    // 调用onTouchEvent方法
    setTouchEventListener(this);
  }

  public MusicPlayerView(Context context, AttrSet attrs, String defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context);

    // 获取屏幕的宽高度、中心点坐标，调用onEstimateSize方法
    setEstimateSizeListener(this);
    // 添加绘制任务，调用onDraw方法
    addDrawTask(this);
    // 调用onTouchEvent方法
    setTouchEventListener(this);
  }

  /**
   * Initializes resource values, create objects which we need them later.
   * Object creation must not called onDraw() method, otherwise it won't be
   * smooth.
   */
  private void init(Context context) {
    mRotateDegrees = 0;
    initPaint();
  }

  /**
   * Calculate mWidth, mHeight, mCenterX, mCenterY values and
   * scale resource bitmap. Create shader. This is not called multiple times.
   */
  @Override
  public boolean onEstimateSize(int i, int i1) {
    mWidth = EstimateSpec.getSize(i);
    mHeight = EstimateSpec.getSize(i1);
    int minSide = Math.min(mWidth, mHeight);
    mWidth = minSide;
    mHeight = minSide;
    initCover();
    mCenterX = mWidth / 2f;
    mCenterY = mHeight / 2f;

    //set RectF left, top, right, bottom coordiantes
    if(rectF==null){
      rectF=new RectFloat();
    }
    rectF.modify(20.0f, 20.0f, mWidth - 20.0f, mHeight - 20.0f);
    //button size is about to 1/4 of image size then we divide it to 8.
    mButtonRadius = mWidth / 8.0f;

    mButtonRegion = new Region( new Rect((int) (mCenterX - mButtonRadius), (int) (mCenterY - mButtonRadius),
            (int) (mCenterX + mButtonRadius), (int) (mCenterY + mButtonRadius)));
    //We resize play/pause drawable with button radius. button needs to be inside circle.
    setEstimatedSize(
            EstimateSpec.getChildSizeWithMode(mWidth, mHeight, EstimateSpec.PRECISE),
            EstimateSpec.getChildSizeWithMode(mWidth, mHeight, EstimateSpec.PRECISE)
    );
    return true;
  }

  boolean isFirst=true;

  /**
   * This is where magic happens as you know.
   */
  @Override
  public void onDraw(Component component, Canvas canvas) {
    drawProgress(canvas);
    drawText(canvas);

    drawCover(canvas);
    canvas.drawCircle(mCenterX, mCenterY, mButtonRadius, mPaintButton);
  }




  public void initCover() {
    mBitmapCover = Utils.createPixelMapByResId(ResourceTable.Media_mycover, getContext(), (int) mWidth, (int) mHeight).get();
    PixelMapShader mBitmapShader = new PixelMapShader(new PixelMapHolder(mBitmapCover), Shader.TileMode.CLAMP_TILEMODE, Shader.TileMode.CLAMP_TILEMODE);
    mPaintCover = new Paint();
    mPaintCover.setAntiAlias(true);
    mPaintCover.setShader(mBitmapShader, Paint.ShaderType.PIXELMAP_SHADER);
  }

  public void drawCover(Canvas canvas){
    //Draw cover image
    float radius = mCenterX <= mCenterY ? mCenterX - 75.0f : mCenterY - 75.0f;
    canvas.rotate(mRotateDegrees, mCenterX, mCenterY);
    canvas.drawCircle(mCenterX, mCenterY, radius, mPaintCover);
  }

  public void drawProgress(Canvas canvas){
    //Draw empty progress
    canvas.drawArc(rectF,new Arc(145, 250, false),mPaintProgressEmpty);
    //Draw loaded progress
    canvas.drawArc(rectF, new Arc(145, calculatePastProgressDegree(), false), mPaintProgressLoaded);
  }

  public void drawText(Canvas canvas){
    //Draw left time text
    String leftTime = secondsToTime(calculateLeftSeconds());
    mRectText=mPaintTime.getTextBounds(leftTime);

    canvas.drawText(mPaintTime,leftTime, (float) (mCenterX * Math.cos(Math.toRadians(35.0))) + mWidth / 2.0f
                    - mRectText.getWidth() / 1.5f,
            (float) (mCenterX * Math.sin(Math.toRadians(35.0))) + mHeight / 2.0f + mRectText.getHeight()
                    + 15.0f);

    //Draw passed time text
    String passedTime = secondsToTime(calculatePassedSeconds());
    mPaintTime.getTextBounds(passedTime);

    canvas.drawText(mPaintTime,passedTime,
            (float) (mCenterX * -Math.cos(Math.toRadians(35.0))) + mWidth / 2.0f
                    - mRectText.getWidth() / 3.0f,
            (float) (mCenterX * Math.sin(Math.toRadians(35.0))) + mHeight / 2.0f + mRectText.getHeight()
                    + 15.0f);
  }



  /**
   * Calculate passed progress degree
   */
  private int calculatePastProgressDegree() {
    return (250 * currentProgress) / maxProgress;
  }

  /**
   * Update rotate degree of cover and invalide onDraw();
   */
  public void updateCoverRotate() {
    mRotateDegrees += VELOCITY;
    mRotateDegrees = mRotateDegrees % 360;
  }

  /**
   * Start turning image
   */
  long time;
  public void start() {
    isRotating = true;
    startProgress();
    startRotate();
  }
  /**
   * Stop turning image
   */
  public void stop() {
    isRotating = false;
  }

  Timer mHandlerRotate = new Timer();

  public void startRotate() {
    if (mHandlerRotate != null) {
      mHandlerRotate.cancel();
      mHandlerRotate = new Timer();
    }
    mHandlerRotate.schedule(
            new TimerTask() {

              @Override

              public void run() {
                updateRotate();
              }
            },

            0, ROTATE_DELAY);
  }

  private void updateRotate() {
    if (isRotating ) {
      if (currentProgress > maxProgress) {
        currentProgress = 0;
        setProgress(currentProgress);
      }
      updateCoverRotate();
    }
    getContext().getUITaskDispatcher().delayDispatch(new Runnable() {
      @Override
      public void run() {
        invalidate();
      }
    }, 0);
  }

  public void setProgress(int currentProgress) {
    if (0 <= currentProgress && currentProgress <= maxProgress) {
      this.currentProgress = currentProgress;
    }
  }
  /**
   * Handler for posting runnable object
   */
  Timer timer = new Timer();

  boolean f;
  public void startProgress() {
    if (timer != null) {
      timer.cancel();
      timer = new Timer();
      f=true;
    }
    timer.schedule(
            new TimerTask() {

              @Override

              public void run() {
                if(f){
                  f=false;
                }else{
                  updateProgress();
                }

              }
            },

            0, PROGRESS_SECOND_MS);
  }

  private void updateProgress() {
    getContext().getUITaskDispatcher().delayDispatch(new Runnable() {
      @Override
      public void run() {
        if (isRotating) {
          currentProgress++;
        }
        invalidate();

      }
    }, 0);
  }


  public void initPaint(){
    //Play/Pause button circle paint
    mPaintButton = new Paint();
    mPaintButton.setAntiAlias(true);
    mPaintButton.setStyle(Paint.Style.FILL_STYLE);
    mPaintButton.setColor(mButtonColor);

    //Progress paint object creation
    mPaintProgressEmpty = new Paint();
    mPaintProgressEmpty.setAntiAlias(true);
    mPaintProgressEmpty.setColor(mProgressEmptyColor);
    mPaintProgressEmpty.setStyle(Paint.Style.STROKE_STYLE);
    mPaintProgressEmpty.setStrokeWidth(12.0f);

    mPaintProgressLoaded = new Paint();
    mPaintProgressLoaded.setAntiAlias(true);
    mPaintProgressLoaded.setColor(mProgressLoadedColor);
    mPaintProgressLoaded.setStyle(Paint.Style.STROKE_STYLE);
    mPaintProgressLoaded.setStrokeWidth(12.0f);

    mPaintTime = new Paint();
    mPaintTime.setColor(mTextColor);
    mPaintTime.setAntiAlias(true);
    mPaintTime.setTextSize(mTextSize);

    //rectF and rect initializes
    rectF = new RectFloat();
    mRectText = new Rect();

  }

  /**
   * Checks is rotating
   */
  public boolean isRotating() {
    return isRotating;
  }





  /**
   * Set velocity.When updateCoverRotate() method called,
   * increase degree by velocity value.
   */
  public void setVelocity(int velocity) {
    if (velocity > 0) VELOCITY = velocity;
  }


  /**
   * This is detect when mButtonRegion is clicked. Which means
   * play/pause action happened.
   */
  @Override
  public boolean onTouchEvent(Component component, TouchEvent touchEvent) {

    float x = touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
    float y = touchEvent.getPointerPosition(touchEvent.getIndex()).getY();

    switch (touchEvent.getAction()) {
      case TouchEvent.PRIMARY_POINT_DOWN: {
        return true;
      }
      case TouchEvent.PRIMARY_POINT_UP: {
        if (mButtonRegion.contains((int) x, (int) y)) {

        }
      }
      break;

      default: break;
    }

    return true;
  }
  /**
   * Calculate left seconds
   */
  private int calculateLeftSeconds() {
    return maxProgress - currentProgress;
  }

  /**
   * Return passed seconds
   */
  private int calculatePassedSeconds() {
    return currentProgress;
  }


  /**
   * Convert seconds to time
   */
  private String secondsToTime(int seconds) {
    String time = "";

    String minutesText = String.valueOf(seconds / 60);
    if (minutesText.length() == 1) minutesText = "0" + minutesText;

    String secondsText = String.valueOf(seconds % 60);
    if (secondsText.length() == 1) secondsText = "0" + secondsText;

    time = minutesText + ":" + secondsText;

    return time;
  }

}
